1 Executive summary

  • Analiza danych materiałów do superkondensatorów rozpoczęła się od wczytania i oczyszczenia danych, w tym uzupełnienia brakujących wartości – dane numeryczne zastąpiono medianą, a tekstowe wartości „NA”. Wstępne podsumowanie wykazało dużą różnorodność zmiennych numerycznych oraz kategorycznych.
  • Szczegółowa analiza rozkładów zmiennych numerycznych pokazała, że większość atrybutów jest silnie skośna, a w niektórych kolumnach pojawiają się obserwacje odstające, które mogą odpowiadać zarówno błędom pomiarowym, jak i wartościom o szczególnym znaczeniu naukowym.
  • Analiza korelacji zmiennych wykazała m.in., że c_at_percent i o_at_percent są silnie ujemnie skorelowane (-0.84), co wskazuje na odwrotną zależność między zawartością węgla i tlenu - więcej tlenu może zwiększać pojemność pseudopojemnościową, ale zmniejszać przewodność.
  • Interaktywne wykresy pozwoliły podsumować trendy: bardzo mała powierzchnia właściwa elektrody powoduje dużą rozpiętość pojemności, natomiast większa powierzchnia (>4 m²/g) utrzymuje pojemność na niskim poziomie. Z kolei większe okno potencjału wiąże się ze spadkiem maksymalnej pojemności. To może sugerować, że nadmierna powierzchnia elektrody nie sprzyja zwiększeniu pojemności – możliwe przyczyny: słaba przewodność materiału, zbyt duże pory, ograniczenia elektrolitu.
  • Model Random Forest przewidujący pojemność na podstawie pozostałych zmiennych numerycznych, analizowany za pomocą DALEX, wykazał, że najważniejszą cechą determinującą pojemność jest potential_window_v, co podkreśla kluczową rolę szerokości okna potencjału elektrody przy projektowaniu materiałów do superkondensatorów.
  • Wiodący wniosek: spośród wszystkich badanych parametrów to okno potencjału elektrody (potential_window_v) jest najistotniejszym czynnikiem wpływającym na pojemność, co ma kluczowe znaczenie dla optymalizacji projektów superkondensatorów.

2 Wykorzystane biblioteki

library(tidyverse)   
library(knitr)       
library(kableExtra) 
## systemfonts and textshaping have been compiled with different versions of Freetype. Because of this, textshaping will not use the font cache provided by systemfonts
library(readr)       
library(janitor)   
library(lubridate)   
library(ggplot2)      
library(plotly)      
library(corrplot)     
library(ggthemes)     
library(DALEX)       
library(randomForest)
library(reshape2)

3 Powtarzalność wyników

knitr::opts_chunk$set(
  echo = TRUE,      
  message = FALSE,  
  warning = FALSE,  
  cache = FALSE     
)
set.seed(12345) 

4 Wczytanie danych

plik <- "C:/Users/jozwi/Documents/raport_superkondensatory/data/data.csv"
df <- read_csv(plik, guess_max = 10000) %>% clean_names()

head(df,5)

5 Przetwarzanie brakujących danych

missing_summary <- df %>%
  summarise(across(everything(), ~ sum(is.na(.)))) %>%
  pivot_longer(cols = everything(), names_to = "column", values_to = "missing_count") %>%
  arrange(desc(missing_count))

knitr::kable(missing_summary, caption = "Braki danych w kolumnach") %>%
  kableExtra::kable_styling(full_width = F)
Braki danych w kolumnach
column missing_count
charge_transfer_resistance_rct_ohm 786
equivalent_series_resistance_rs_ohm 772
pore_size_nm 769
pore_volume_cm_3_g 729
o_at_percent 703
c_at_percent 699
n_at_percent 690
ratio_of_id_ig 596
specific_surface_area_m_2_g 572
electrolyte_ionic_conductivity 99
electrolyte_concentration_m 62
electrolyte_chemical_formula 22
capacitance_f_g 17
current_density_a_g 16
cell_configuration_three_two_electrode_system 14
potential_window_v 5
limits_of_potential_window_v 4
lower_limit_of_potential_window_v 4
upper_limit_of_potential_window_v 4
ref 0
electrode_configuration 0
df_clean <- df %>%
  mutate(
    across(where(is.numeric), ~ ifelse(is.na(.), median(., na.rm = TRUE), .)),
    across(where(is.character), ~ ifelse(is.na(.), "NA", .))
  )

cat("Łączna liczba NA po przetworzeniu:", sum(is.na(df_clean)), "\n")
## Łączna liczba NA po przetworzeniu: 0

W przypadki jeśli kolumna ma charakter numeryczny pusta komórka jest wypełniena medianą, jeśli charakter tekstowy to jest wypełniana “NA”. Końcowy komunikat ukazuje pozbycie się pustych danych.

6 Podsumowanie rozmiaru zbioru i podstawowe statystyki

cat("Rozmiar zbioru danych:", nrow(df_clean), "wierszy i", ncol(df_clean), "kolumn\n")
## Rozmiar zbioru danych: 925 wierszy i 21 kolumn

6.1 Kolumny numeryczne

numeric_cols <- df_clean %>% select(where(is.numeric))
num_summary <- data.frame(
  Column = character(),
  Min = numeric(),
  Median = numeric(),
  Mean = numeric(),
  Max = numeric(),
  SD = numeric(),
  stringsAsFactors = FALSE
)

for(col in names(numeric_cols)){
  num_summary <- rbind(num_summary, data.frame(
    Column = col,
    Min = min(numeric_cols[[col]], na.rm = TRUE),
    Median = median(numeric_cols[[col]], na.rm = TRUE),
    Mean = round(mean(numeric_cols[[col]], na.rm = TRUE), 2),
    Max = max(numeric_cols[[col]], na.rm = TRUE),
    SD = round(sd(numeric_cols[[col]], na.rm = TRUE), 2),
    stringsAsFactors = FALSE
  ))
}

knitr::kable(num_summary, caption = "Podstawowe statystyki kolumn numerycznych") %>%
  kableExtra::kable_styling(full_width = F)
Podstawowe statystyki kolumn numerycznych
Column Min Median Mean Max SD
lower_limit_of_potential_window_v -1.100 0.00000 -0.23 0.200 0.37
upper_limit_of_potential_window_v -0.200 0.60000 0.63 3.500 0.45
potential_window_v 0.400 0.82500 0.86 3.500 0.35
current_density_a_g 0.050 2.00000 5.79 200.000 13.25
capacitance_f_g 1.400 260.25000 412.65 3344.080 443.88
specific_surface_area_m_2_g 8.896 159.97000 258.23 2400.000 359.82
charge_transfer_resistance_rct_ohm 0.080 1.54000 1.77 24.200 1.86
equivalent_series_resistance_rs_ohm 0.200 0.58000 0.75 17.500 1.06
pore_size_nm 0.530 4.33650 5.06 44.131 3.68
pore_volume_cm_3_g 0.020 0.21705 0.27 2.350 0.29
ratio_of_id_ig 0.120 1.05000 1.08 2.900 0.26
n_at_percent 0.000 0.00000 0.64 23.820 2.55
c_at_percent 1.400 81.00000 77.46 98.100 15.45
o_at_percent 1.900 13.70000 15.01 54.280 7.46
electrolyte_ionic_conductivity 1.000 6.00000 5.83 8.000 1.32
electrolyte_concentration_m 0.100 1.00000 2.47 6.000 2.16

6.2 Kolumny kategoryczne

categorical_cols <- df_clean %>% select(where(is.character))


unique_counts <- sapply(categorical_cols, function(col) length(unique(col)))

categorical_summary <- data.frame(
  Column = names(unique_counts),
  Unique_Values = as.integer(unique_counts),
  stringsAsFactors = FALSE
)

knitr::kable(categorical_summary, caption = "Liczba unikalnych wartości w kolumnach kategorycznych") %>%
  kableExtra::kable_styling(full_width = F)
Liczba unikalnych wartości w kolumnach kategorycznych
Column Unique_Values
ref 198
limits_of_potential_window_v 64
electrode_configuration 353
electrolyte_chemical_formula 24
cell_configuration_three_two_electrode_system 3

6.3 Obserwacje

  • Kolumny numeryczne są bardzo różnorodne, rozpiętość np. kolumny capacitance_f_g ma wartości od 1.4 do 3344.08.
  • Kolumna cell_configuration_three_two_electrode_system ma tylko 3 unikalne wartości – idealne do analizy porównawczej.
  • Kolumna electrode_configuration ma 353 unikalne wartości – bardzo różnorodne, może wymagać grupowania do wizualizacji.

7 Analiza atrybutów

7.1 Histogramy oraz wykresy pudełkowe dla atrybutów numerycznych

numeric_cols <- df_clean %>% select(where(is.numeric))

for(col in names(numeric_cols)){
  
  # Histogram
  p_hist <- ggplot(df_clean, aes_string(x = col)) +
    geom_histogram(bins = 30, fill = "#2c7fb8", color = "white") +
    theme_minimal() +
    labs(title = paste("Histogram rozkładu:", col),
         x = col,
         y = "Liczba próbek")
  
  print(p_hist)
  
  # Boxplot
  p_box <- ggplot(df_clean, aes_string(y = col)) +
    geom_boxplot(fill = "#fdae61", color = "black") +
    theme_minimal() +
    labs(title = paste("Wykres pudełkowy:", col),
         y = col,
         x = "")
  
  print(p_box)
}

ggplot(df_clean, aes(x = cell_configuration_three_two_electrode_system,
y = capacitance_f_g,
fill = cell_configuration_three_two_electrode_system)) +
geom_boxplot() +
theme_minimal() +
labs(title = "Porównanie pojemności w zależności od konfiguracji ogniwa",
x = "Konfiguracja ogniwa (3/2 elektrody)",
y = "Pojemność (F/g)") +
scale_fill_brewer(palette = "Set2") +
theme(legend.position = "none")

7.2 Obserwacje analizy atrybutu

  • W kolumnach upper_limit_of_potential_window_v i pootencial window_v rozkład wartości jest silnie nieregularny.
  • W kolumnach current_density_a_g, capacitance_f_g można zauważyć duży spadek ilości próbek wraz ze wzrostem wartości mierzonej.
  • W reszcie kolumn występuje rozkład silnie skośny, większość obserwacji skupia się wokół jednej (max dwóch) wartości.
  • Z wielkości zółtych pól na wykresach pudełkowych można łatwo odczytać zróżnicowanie atrybutów. Na przykład zmienność atrybutu electrolyte_concentration_m jest bardzo duża, w porównaniu z electrolyte_concentration_m.
  • Z wykresów pudełkowych można także odczytać, które z atrybutów mają stosunkowo dużo wartości odstających. Jest to istotny aspekt badania superkondensatorów, ponieważ mogą świadczyć np. o najlepszych parametrach lub uszkodzeniach
  • Z wykresu “Porównanie pojemności w zależności od konfiguracji ogniwa” możemy wywnioskować, że układ 3 elektrodowy ma najwięcej wartości odstjących

8 Korelacje między zmiennymi

8.1 Macierz korelacji

numeric_cols <- df_clean %>% select(where(is.numeric))
cor_matrix <- cor(numeric_cols, use = "complete.obs", method = "pearson")
corrplot(cor_matrix, method = "color",
         type = "upper",
         tl.cex = 0.7,
         tl.col = "black",
         addCoef.col = "black",
         number.cex = 0.6,
         title = "Macierz korelacji zmiennych numerycznych",
         mar = c(0,0,1,0))

8.1.1 Obserwacje macierzy korelacji

  • Korelacja atrybutów c_at_percent i o_at_percent wynosi -0.84. Wzrost zawartości węgla wiąże się ze spadkiem zawartości tlenu i odwrotnie.
  • Korelacja atrybutów charge_transfer_resistance_rct_ohm i equivalent_series_resistance_rs_ohm wynosi 0.62, co oznacza, że wzrost oporu przeniesienia ładunku (Rct) wiąże się ze wzrostem oporu szeregowego (Rs).
  • Korelacja atrybutów potential_window_v i capacitance_f_g wynosi -0.34. Można ją zinterpretować w ten sposób: szerszy zakres potencjału może nieco obniżać pojemność elektrody.

9 Interaktywne wykresy

9.1 Wykres zależność powierzchni właściwej i pojemności

p <- plot_ly(df_clean, 
             x = ~specific_surface_area_m_2_g, 
             y = ~capacitance_f_g,
             type = 'scatter',
             mode = 'markers',
             color = ~ratio_of_id_ig,
             colors = colorRamp(c("blue", "red")),
             size = ~pore_volume_cm_3_g,
             sizes = c(5, 30),
             text = ~paste("Ref.:", ref,
                           "<br>Electrolyte:", electrolyte_chemical_formula,
                           "<br>Current density:", current_density_a_g,
                           "<br>Pore size (nm):", pore_size_nm),
             hoverinfo = "text") %>%
  layout(title = "Wykres 1: zależność powierzchni właściwej i pojemności",
         xaxis = list(title = "Powierzchnia właściwa (m²/g)"),
         yaxis = list(title = "Pojemność (F/g)"),
         legend = list(title = list(text = "Ratio ID/IG")))

p

9.1.1 Obserwacje do wykresu

  • Na wykresie dla bardzo małej powierzchni Widzimy bardzo dużą rozpiętość pojemności. Dla większej powierzchni (>4 m²/g) pojemność utrzymuje się na niskim poziomie.

9.2 Wykres zależności okna potencjału i pojemności

p2 <- plot_ly(df_clean,
              x = ~potential_window_v,
              y = ~capacitance_f_g,
              type = 'scatter',
              mode = 'markers',
              color = ~ratio_of_id_ig,
              colors = colorRamp(c("green", "red")),
              size = ~pore_size_nm,
              sizes = c(5, 30),
              text = ~paste("Ref.:", ref,
                            "<br>Electrolyte:", electrolyte_chemical_formula,
                            "<br>Surface area:", specific_surface_area_m_2_g,
                            "<br>Current density:", current_density_a_g),
              hoverinfo = "text") %>%
  layout(title = "Wykres 2: zależność okna potencjału i pojemności",
         xaxis = list(title = "Okno potencjału (V)"),
         yaxis = list(title = "Pojemność (F/g)"),
         legend = list(title = list(text = "Ratio ID/IG")))

p2

9.2.1 Obserwacje do wykresu

  • Na wykresie widać tendencję spadkową: im większe okno potencjału, tym maksymalna pojemność elektrody spada.

9.3 Wykres 3D zależności pojemności, okna potencjału i rozmiaru porów.

p3 <- plot_ly(df_clean,
              x = ~potential_window_v,
              y = ~capacitance_f_g,
              z = ~pore_size_nm,
              type = 'scatter3d',
              mode = 'markers',
              color = ~ratio_of_id_ig,
              colors = colorRamp(c("blue", "red")),
              size = ~specific_surface_area_m_2_g,
              sizes = c(5, 30),
              text = ~paste("Ref.:", ref,
                            "<br>Electrolyte:", electrolyte_chemical_formula,
                            "<br>Current density:", current_density_a_g),
              hoverinfo = "text") %>%
  layout(title = "Wykres 3: pojemność, okno potencjału i rozmiar porów",
         scene = list(
           xaxis = list(title = "Okno potencjału (V)"),
           yaxis = list(title = "Pojemność (F/g)"),
           zaxis = list(title = "Rozmiar porów (nm)")
         ),
         legend = list(title = list(text = "Ratio ID/IG")))

p3

10 Predykcja modelu i XAI

Poniżej przedstawione jest trenowanie modelu Random Forest, który przewiduje pojemność elektrody na podstawie pozostałych zmiennych numerycznych materiału. Jednocześnie używa DALEX do określenia, które cechy mają największy wpływ na przewidywaną pojemność.

num_df <- df_clean %>% select(where(is.numeric))
y <- num_df$capacitance_f_g
X <- num_df %>% select(-capacitance_f_g)

set.seed(123)
rf_model <- randomForest(X, y, ntree = 500)

explainer_rf <- DALEX::explain(rf_model,
                               data = X,
                               y = y,
                               label = "RandomForest_Capacitance")
## Preparation of a new explainer is initiated
##   -> model label       :  RandomForest_Capacitance 
##   -> data              :  925  rows  15  cols 
##   -> data              :  tibble converted into a data.frame 
##   -> target variable   :  925  values 
##   -> predict function  :  yhat.randomForest  will be used (  default  )
##   -> predicted values  :  No value for predict function target column. (  default  )
##   -> model_info        :  package randomForest , ver. 4.7.1.2 , task regression (  default  ) 
##   -> predicted values  :  numerical, min =  20.36265 , mean =  413.7323 , max =  2837.314  
##   -> residual function :  difference between y and yhat (  default  )
##   -> residuals         :  numerical, min =  -731.6033 , mean =  -1.086883 , max =  1256.267  
##   A new explainer has been created!
vip <- DALEX::model_parts(explainer_rf)
plot(vip) + ggtitle("Wpływ zmiennych na predykcję pojemności (Random Forest)")

pred <- predict(rf_model, X[1:5,])
pred
##        1        2        3        4        5 
## 496.7101 315.2344 306.2953 290.9368 284.5305

10.1 Obserwacje

  • Analiza XAI pokazała, że zmienna potential_window_v ma największy wpływ na przewidywaną pojemność materiałów do superkondensatorów.